#pragma once
#include <vector>

#include "lbvh_structure.h"
#include <geometry\primitive.h>
#include <utility\cuda_utils.h>
#include <collision\collision_structure.h>
#include <physics\rigid_body.h>


namespace gc {
	class LBvh {
	private:
		LBvhLeaves leaves, leaves_copy;
		LBvhInteriors interiors, interiors_copy;
		int primSize;
		SimpleSphere *d_prims;
		
	public:
		LBvh();
		~LBvh();

		void loadPrimitives(SimpleSphere *d_prims, int size);

		SimpleSphere* getDevicePrimsPtr();

		void free();

		void build();

		void testBuild();

		void update();

		void broadPhaseCollison(CollisionPair *d_cp, int *size);
	};


	// combined from app/bouncing_ball_simulation.h
	

	inline Float genRand(Float L, Float R) {
		return L + (R - L)*Float(rand()) / RAND_MAX;
	}

	inline Point3f uniformSampleDirection() {
		Float theta = genRand(0, PI);
		Float phi = genRand(0, 2 * PI);
		Float sintheta = sin(theta);
		Float costheta = cos(theta);
		return { sintheta*cos(phi),costheta,sintheta*sin(phi) };
	}

	class BouncingBallSimulation {
	public:
		SimpleBox sceneBox;
		Float lx, ly, lz;
		Point3f base;
		int n_prims;
		LBvh lbvh;
		thrust::device_vector<SimpleSphere> d_balls;
		thrust::host_vector<SimpleSphere> h_balls;
		MPhys::RigidBody* d_rigids;
		thrust::device_vector<CollisionPair> d_cp_total;

		Float dt;  // time per frame in seconds
		int frame;

		FILE *fLog;
		Float kernelTime;  // time in GPU
		Float cdTime;      // time of broad phase CD kernel
		Float buildTime;
		Float updateTime;
		Float narrowphaseTime;
	public:
		BouncingBallSimulation(SimpleBox &sceneBox, FILE* fLog=stderr);
		~BouncingBallSimulation();

		void setTimePerFrame(Float dt);

		void generateBalls(int number, int maxVelocity);

		void produceFrame();

		// using O(n^2) brutal force
		void produceFrameBrutal();

		void setOutputPath(char *name);

		Float timePerFrame();

		Float timePerBroadPhase();

		void printTimeInfo();
	};
	
}